package proxy_dai_li;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

import sun.misc.ProxyGenerator;

/**
 * 动态代理演示
 * 
 * java 动态代理，具体有如下四步骤：
 * 
 * 1.通过实现 InvocationHandler 接口创建自己的调用处理器；
 * 2.通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类；
 * 3.通过反射机制获得动态代理类的构造函数，其唯一参数类型是调用处理器接口类型；
 * 4.通过构造函数创建动态代理类实例，构造时调用处理器对象作为参数被传入
 * 
 */
public class DynamicProxyDemonstration {
	public static void main(String[] args) {
		//代理的真实对象
		Subject realSubject = new SubjectImp();
		System.out.println("动态代理前对象的类型：" + realSubject.getClass().getName());
		/**
		 * InvocationHandlerImpl 实现了 InvocationHandler 接口，并能实现方法调用从代理类到委托类的分派转发
		 * 其内部通常包含指向委托类实例的引用，用于真正执行分派转发过来的方法调用.
		 * 即：要代理哪个真实对象，就将该对象传进去，最后是通过该真实对象来调用其方法
		 */
		InvocationHandler handler = new InvocationHandlerImpl(realSubject);

		ClassLoader loader = realSubject.getClass().getClassLoader();
		Class[] interfaces = realSubject.getClass().getInterfaces();
		/**
		 * 该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
		 */
		Subject subject = (Subject) Proxy.newProxyInstance(loader, interfaces, handler);

		System.out.println("动态代理对象的类型：" + subject.getClass().getName());

		String hello = subject.SayHello("zhaokai");
		System.out.println(hello);
		//		String goodbye = subject.SayGoodBye();
		//		System.out.println(goodbye);
	}

	/**
	 * 测试使用创建类加载器得到的class文件
	 */
	private static void createProxyClassFile() {
		String name = "ProxySubject";//生成代理类的类名
		/*
		 * ProxyGenerator.generateProxyClass这个方法是动态代理生成代理类的关建方法
		 * (https://www.cnblogs.com/liuyun1995/p/8144706.html)
		 * 该静态方法的核心内容就是去调用generateClassFile()实例方法来生成Class文件
		 * 分为三步实现：
		 * 第一步：收集所有要生成的代理方法，将其包装成ProxyMethod对象并注册到Map集合中。
		 * 第二步：收集所有要为Class文件生成的字段信息和方法信息。
		 * 第三步：完成了上面的工作后，开始组装Class文件
		 */
		byte[] data = ProxyGenerator.generateProxyClass(name, new Class[] { Subject.class });
		FileOutputStream out = null;
		try {
			out = new FileOutputStream(name + ".class");
			System.out.println((new File("hello")).getAbsolutePath());
			out.write(data);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (null != out)
				try {
					out.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
		}
	}

}
